/*
 * Copyright (c) 2007 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 * 
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */
/*
 * This file includes modifications of code found in the Apple XNU Kernel
 * and has been modifed for use in this Kext by Rick Macklem, May 2006.
 */
/*
 * This is the kext version of the newnfs client. It mostly just defines
 * storage and includes other files. Including all the *.c files here is
 * a bit cheezey, but it allows storage to be defined here statically and
 * all the non-locals being included here. (Someday I hope that I'll
 * only need include files in /System/Library/Frameworks/Kernel.framework,
 * but that may take a while...
 */
#define	KERNEL
#define	KERNEL_PRIVATE
#define	XNU_KERNEL_PRIVATE
#define	PRIVATE
#define	ARCH_PRIVATE
#define	BSD_KERNEL_PRIVATE
#define MACH_KERNEL
#define	NFSCL
#include <mach/mach_types.h>

#include <sys/unistd.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/dir.h>
#include <sys/domain.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/kernel.h>
#include <sys/lockf.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/mount_internal.h>
#include <sys/namei.h>
#include <sys/proc_internal.h>
#include <sys/protosw.h>
#define	NFSSERVER	1
#include <sys/sysproto.h>
#include <sys/syscall.h>
#undef NFSSERVER
#include <sys/reboot.h>
#include <sys/resourcevar.h>
#include <sys/signalvar.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/stat.h>
#include <sys/syslog.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/vnode.h>
#include <sys/vnode_internal.h>
#include <sys/buf.h>
#include <sys/tprintf.h>
#include <sys/uio_internal.h>
#include <sys/kauth.h>
#include <sys/kdebug.h>
#include <sys/ubc.h>
#include <sys/ubc_internal.h>
#include <sys/file_internal.h>
#include <sys/sysent.h>
#include <kern/clock.h>
#include <kern/thread.h>
#include <kern/task.h>
#include <kern/kalloc.h>
#include <libkern/OSAtomic.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#undef KERNEL_PRIVATE
#include <netinet6/ipsec.h>
#define KERNEL_PRIVATE
#include <netinet/in_pcb.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_seq.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <crypto/des/des.h>
#include <crypto/des/des_locl.h>
#include <sys/md5.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/ufsmount.h>
#include <vfs/vfs_support.h>
#include <bsm/audit_kernel.h>
#include <miscfs/fifofs/fifo.h>
#include <miscfs/specfs/specdev.h>

#include <mach/task.h>
#include <mach/task_special_ports.h>
#include <mach/host_priv.h>
#include <mach/thread_act.h>
#include <mach/mig_errors.h>
#include <mach/vm_map.h>
#include <vm/vm_map.h>
#include <vm/vm_kern.h>
#include <gssd/gssd_mach_types.h>

#define	APPLEKEXT	1050
#define	APPLESTATIC	static

/*
 * Don't define this as VT_NFS! I did and it was great fun hunting down the bug:-)
 * (Hint. Look in xnu-792.X.Y/bsd/vfs/vfs_subr.c for VT_NFS.) The comment in vnode.h
 * says to not use these in the kernel, but...
 */
#define	VT_NEWNFS	VT_OTHER

#include "nfsport.h"

/*
 * Now, define all the global functions as static.
 */
struct uio;
struct ucred;
struct nfscred;
NFSPROC_T;
struct buf;
struct nfs_diskless;
struct sockaddr_in;
struct nfs_dlmount;
struct nfsd;
struct file;
struct nfssvc_sock;
struct nfsmount;
struct socket;
struct nfsreq;
struct nfssockreq;
struct vattr;
struct nameidata;
struct nfsnode;
struct nfsfh;
struct sillyrename;
struct componentname;
struct nfsd_srvargs;
struct nfsrv_descript;
struct nfs_fattr;
union nethostaddr;
struct nfsstate;
struct nfslock;
struct nfsclient;
struct nfslockconflict;
struct nfsd_idargs;
struct nfsd_clid;
struct nfsusrgrp;
struct nfsgss_handleref;
struct nfsgss_handlebuf;
struct nfsclowner;
struct nfsclopen;
struct nfsclopenhead;
struct nfsclclient;
struct nfscllockowner;
struct nfscllock;
struct nfscldeleg;
struct nfsgss_clhandle;
struct nfsv4lock;
struct nfsvattr;
struct nfs_vattr;
struct NFSSVCARGS;
struct acl_entry;

/* newnfs_socket.c */
static int newnfs_request(struct nfsrv_descript *, struct nfsmount *,
    struct nfsclient *, struct nfssockreq *, vnode_t, NFSPROC_T *,
    struct ucred *, u_int32_t, u_int32_t, u_char *, int, u_int64_t *);
#ifndef newnfs_realign
static void newnfs_realign(mbuf_t *, int);
#endif
static int newnfs_connect(struct nfsmount *, struct nfssockreq *,
    struct nfsreq *, struct ucred *, NFSPROC_T *);
static void newnfs_disconnect(struct nfssockreq *);
void newnfs_timer(void *);
static int newnfs_sigintr(struct nfsmount *, struct nfsreq *, NFSPROC_T *);
static int newnfs_sndlock(int *, struct nfsreq *);
static void newnfs_sndunlock(int *);
static void nfsreq_free(struct nfsreq *);
static void nfsrvd_rephead(struct nfsrv_descript *);
static int nfsrvd_getreq(struct nfsrv_descript *, NFSPROC_T *, int);
static void nfsrv_replyheader(struct nfsrv_descript *, struct nfssvc_sock *,
    NFSPROC_T *);

/* newnfs_subs.c */
static void newnfs_init(void);
static int nfsaddr_match(int, union nethostaddr *, NFSSOCKADDR_T);
static int nfsaddr2_match(NFSSOCKADDR_T, NFSSOCKADDR_T);
static int nfsm_strtom(struct nfsrv_descript *, const char *, int);
static int nfsm_mbufuio(struct nfsrv_descript *, struct uio *, int);
static int nfsm_fhtom(struct nfsrv_descript *, u_int8_t *, int, int);
static int nfsm_advance(struct nfsrv_descript *, int, int);
static void newnfs_rpchead(struct ucred *, struct nfsreq *);
static void newnfs_trimleading(struct nfsrv_descript *);
static void newnfs_trimtrailing(struct nfsrv_descript *, mbuf_t,
    caddr_t);
static void newnfs_copycred(struct nfscred *, struct ucred *);
static void newnfs_copyincred(struct ucred *, struct nfscred *);
static int nfsrv_dissectacl(struct nfsrv_descript *, kauth_acl_t *, int *,
    int *, NFSPROC_T *);
static int nfsrv_getattrbits(struct nfsrv_descript *, nfsattrbit_t *, int *,
    int *);
static int nfsv4_loadattr(struct nfsrv_descript *, vnode_t,
    struct nfsvattr *, struct nfsfh **, fhandle_t *, int,
    struct nfsv3_pathconf *, struct statfs *, struct nfsstatfs *,
    struct nfsfsinfo *, kauth_acl_t *,
    int, int *, u_int32_t *, u_int32_t *, NFSPROC_T *, struct ucred *);
static int nfsv4_lock(struct nfsv4lock *, int, int *, void *);
static void nfsv4_unlock(struct nfsv4lock *, int);
static void nfsv4_relref(struct nfsv4lock *);
static void nfsv4_getref(struct nfsv4lock *, int *, void *);
static int nfsrv_mtostr(struct nfsrv_descript *, char *, int);
static int nfsrv_checkutf8(u_int8_t *, int);
static void nfsv4_uidtostr(uid_t, u_char **, int *, NFSPROC_T *);
static int nfsv4_strtouid(u_char *, int, uid_t *, NFSPROC_T *);
static void nfsv4_gidtostr(gid_t, u_char **, int *, NFSPROC_T *);
static int nfsv4_strtogid(u_char *, int, gid_t *, NFSPROC_T *);
static int nfsrv_putattrbit(struct nfsrv_descript *, nfsattrbit_t *);

/* nfscl_subs.c */
static void nfsm_uiombuf(struct nfsrv_descript *, struct uio *, int);
static void nfscl_reqstart(struct nfsrv_descript *, int, struct nfsmount *,
    u_int8_t *, int, u_int32_t **);
static nfsuint64 *nfscl_getcookie(struct nfsnode *, off_t off, int);
static void nfscl_fillsattr(struct nfsrv_descript *, struct vattr *,
      vnode_t, int, u_int32_t, struct ucred *, proc_t);
static u_int8_t *nfscl_getmyip(struct nfsmount *, int *);
static int nfsm_getfh(struct nfsrv_descript *, struct nfsfh **);
static int nfscl_mtofh(struct nfsrv_descript *, struct nfsfh **,
        struct nfsvattr *, int *);
static int nfscl_postop_attr(struct nfsrv_descript *, struct nfsvattr *, int *,
    void *);
static int nfscl_wcc_data(struct nfsrv_descript *, vnode_t,
    struct nfsvattr *, int *, int *, void *);
static int nfsm_loadattr(struct nfsrv_descript *, struct nfsvattr *,
    kauth_acl_t *);
static int nfscl_request(struct nfsrv_descript *, vnode_t,
         NFSPROC_T *, struct ucred *, void *);
static void nfsm_stateidtom(struct nfsrv_descript *, nfsv4stateid_t *, int);
static int nfscl_maperr(NFSPROC_T *, int, uid_t);

/* newnfs_syscalls.c */
static int sys_getfh(NFSPROC_T *, void *, register_t *);
static int nfssvc_call(NFSPROC_T *, struct NFSSVCARGS *, struct ucred *);
static int newnfs_iod(NFSPROC_T *);

/* newnfs_gss.c */
static int nfsgss_ckheader(struct nfsrv_descript *, int, int, int,
    struct nfsgss_handleref *, KEYSCHED_T);
static int nfsgss_unwrap(struct nfsrv_descript *, int, int,
    u_char *, KEYSCHED_T);
static int nfsgss_des(mbuf_t, u_char *, int, u_char *, int);
static void nfsgss_desmacmd5(struct nfsrv_descript *, int,
    KEYSCHED_T, int, u_char *);
static int nfsgss_verf(struct nfsrv_descript *,
    KEYSCHED_T, u_int32_t, struct nfsreq *, int, u_char *);
static int nfsgss_createtok(struct nfsrv_descript *, KEYSCHED_T,
    int, u_char *, int, int);
static int nfsgss_lengthtok(struct nfsrv_descript *, int);
static int nfsgss_ckseqnum(struct nfsrv_descript *, struct nfsgss_handle *);
static void nfsgss_wrapcksum(struct nfsrv_descript *, int, u_char *);
static void nfsgss_initcache(void);
static int nfsgss_gethandle(u_char *, struct nfsgss_handle **, int,
    NFSPROC_T *);
static void nfsgss_releasehandle(struct nfsgss_handle *, NFSPROC_T *);
static void nfsgss_destroy(struct nfsgss_handle *);
static int nfsgss_continit(struct nfsrv_descript *,int,struct nfsgss_handle **,
    NFSPROC_T *);
static int nfsgss_addport(u_short, NFSPROC_T *);
static void nfsgss_deleteallports(NFSPROC_T *);
static u_int32_t nfsgss_getclseq(struct nfsgss_handleref *);
static void nfsgss_rcvdclseq(struct nfsreq *);
static void nfsgsscl_gethandle(struct nfsreq *);
static void nfsgsscl_releasehandle(struct nfsreq *, int);
static void nfsgsscl_freehandle(struct nfsmount *, struct nfsgss_clhandle *);
static void nfsgsscl_zaphandle(struct nfsreq *);

/* newnfs_port.c */
static int nfs_msg(NFSPROC_T *, char *, char *);
static int newnfsd_create(vnode_t, vnode_t *,
    struct componentname *, struct vattr *);
static int newnfsd_mknod(vnode_t, vnode_t *,
    struct componentname *, struct vattr *);
static int newnfsd_mkdir(vnode_t, vnode_t *,
    struct componentname *, struct vattr *);
static int newnfsd_symlink(vnode_t, vnode_t *,
    struct componentname *, struct vattr *, caddr_t);
static int newnfsd_link(vnode_t, vnode_t,
    struct componentname *, NFSPROC_T *);
static int newnfsd_remove(vnode_t, vnode_t,
    struct componentname *);
static int newnfsd_rmdir(vnode_t, vnode_t,
    struct componentname *);
static void nfsrv_mntinit(mount_t, NFSPROC_T *);
static int nfsrv_checksockseqnum(struct socket *, u_int32_t);
static int nfsrv_getsockseqnum(struct socket *, u_int32_t *);
static int nfsrv_lookupfilename(struct nameidata *, char *, NFSPROC_T *);
static void nfsrv_object_create(vnode_t, NFSPROC_T *);
static int nfsrv_mallocmget_limit(void);
static int nfsvno_v4rootexport(struct nfsrv_descript *);
static int nfsrvd_dorec(struct nfssvc_sock *, struct nfsd *,
     struct nfsrv_descript *, NFSPROC_T *, int);
static int nfsrvd_getstream(struct nfssvc_sock *, int);
static void newnfs_setsockopt(struct socket *);
static void nfsrvd_socklock(int *);
static void nfsrvd_sockunlock(int *);
static int nfs_sbspace(struct sockbuf *);
static void newnfs_portinit(void);
static struct ucred *newnfs_getcred(void);
static void newnfs_setroot(struct ucred *);

/* newnfs_acl.c */
static int nfsrv_dissectace(struct nfsrv_descript *, kauth_ace_t,
    int *, int *, NFSPROC_T *);
static int nfsrv_buildacl(struct nfsrv_descript *, kauth_acl_t, enum vtype,
    NFSPROC_T *);
static int nfsrv_compareacl(kauth_acl_t, kauth_acl_t);
static kauth_acl_t nfsacl_from_mode(u_short, kauth_acl_t);

/* nfscl_rpcops.c */
static int nfsrpc_null(vnode_t, struct ucred *, NFSPROC_T *);
static int nfsrpc_access(vnode_t, int, struct ucred *, NFSPROC_T *,
    struct nfsvattr *, int *);
static int nfsrpc_accessrpc(vnode_t, u_int32_t, struct ucred *,
    NFSPROC_T *, struct nfsvattr *, int *, u_int32_t *, void *);
static int nfsrpc_open(vnode_t, int, struct ucred *, NFSPROC_T *);
static int nfsrpc_openrpc(struct nfsmount *, vnode_t, u_int8_t *, int, u_int8_t *, int,
    u_int32_t, struct nfsclopen *, u_int8_t *, int, struct nfscldeleg **, int,
    u_int32_t, struct ucred *, NFSPROC_T *, int, int);
static int nfsrpc_opendowngrade(vnode_t, u_int32_t, struct nfsclopen *,
    struct ucred *, NFSPROC_T *);
static int nfsrpc_close(vnode_t, int, NFSPROC_T *);
static void nfsrpc_doclose(struct nfsmount *, struct nfsclopen *, NFSPROC_T *);
static int nfsrpc_closerpc(struct nfsrv_descript *, struct nfsmount *,
    struct nfsclopen *, struct ucred *, NFSPROC_T *, int);
static int nfsrpc_openconfirm(vnode_t, u_int8_t *, int, struct nfsclopen *,
    struct ucred *, NFSPROC_T *);
static int nfsrpc_setclient(struct nfsmount *, struct nfsclclient *,
    struct ucred *, NFSPROC_T *);
static int nfsrpc_getattr(vnode_t, struct ucred *, NFSPROC_T *,
    struct nfsvattr *, void *, kauth_acl_t *);
static int nfsrpc_getattrnovp(struct nfsmount *, u_int8_t *, int, int,
    struct ucred *, NFSPROC_T *, struct nfsvattr *, u_int64_t *);
static int nfsrpc_setattr(vnode_t, struct vattr *, struct ucred *,
    NFSPROC_T *, struct nfsvattr *, int *, void *);
static int nfsrpc_lookup(vnode_t, char *, int, struct ucred *, NFSPROC_T *,
    struct nfsvattr *, struct nfsvattr *, struct nfsfh **, int *, int *,
    void *);
static int nfsrpc_readlink(vnode_t, struct uio *, struct ucred *,
    NFSPROC_T *, struct nfsvattr *, int *, void *);
static int nfsrpc_read(vnode_t, struct uio *, struct ucred *, NFSPROC_T *,
    struct nfsvattr *, int *, void *);
static int nfsrpc_write(vnode_t, struct uio *, int *, u_char *,
    struct ucred *, NFSPROC_T *, struct nfsvattr *, int *, void *);
static int nfsrpc_mknod(vnode_t, char *, int, struct vattr *, u_int32_t,
    enum vtype, struct ucred *, NFSPROC_T *, struct nfsvattr *,
    struct nfsvattr *, struct nfsfh **, int *, int *, void *);
static int nfsrpc_create(vnode_t, char *, int, struct vattr *, nfsquad_t,
    int, struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *,
    struct nfsfh **, int *, int *, void *);
static int nfsrpc_remove(vnode_t, char *, int, vnode_t, struct ucred *, NFSPROC_T *,
    struct nfsvattr *, int *, void *);
static int nfsrpc_rename(vnode_t, vnode_t, char *, int, vnode_t, vnode_t, char *, int,
    struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *,
    int *, int *, void *, void *);
static int nfsrpc_link(vnode_t, vnode_t, char *, int,
    struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *,
    int *, int *, void *);
static int nfsrpc_symlink(vnode_t, char *, int, char *, struct vattr *,
    struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *,
    struct nfsfh **, int *, int *, void *);
static int nfsrpc_mkdir(vnode_t, char *, int, struct vattr *,
    struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *,
    struct nfsfh **, int *, int *, void *);
static int nfsrpc_rmdir(vnode_t, char *, int, struct ucred *, NFSPROC_T *,
    struct nfsvattr *, int *, void *);
static int nfsrpc_readdir(vnode_t, struct uio *, nfsuint64 *, struct ucred *,
    NFSPROC_T *, struct nfsvattr *, int *, int *, void *);
static int nfsrpc_readdirplus(vnode_t, struct uio *, nfsuint64 *, 
    struct ucred *, NFSPROC_T *, struct nfsvattr *, int *, int *, void *);
static int nfsrpc_commit(vnode_t, u_quad_t, int, struct ucred *,
    NFSPROC_T *, u_char *, struct nfsvattr *, int *, void *);
static int nfsrpc_advlock(vnode_t, off_t, int, struct flock *, int,
    struct ucred *, NFSPROC_T *);
static int nfsrpc_lockt(struct nfsrv_descript *, vnode_t,
    struct nfsclclient *, u_int64_t, u_int64_t, struct flock *,
    struct ucred *, NFSPROC_T *);
static int nfsrpc_lock(struct nfsrv_descript *, struct nfsmount *, vnode_t,
    u_int8_t *, int, struct nfscllockowner *, int, int, u_int64_t,
    u_int64_t, short, struct ucred *, NFSPROC_T *, int);
static int nfsrpc_statfs(vnode_t, struct nfsstatfs *, struct nfsfsinfo *,
    struct ucred *, NFSPROC_T *, struct nfsvattr *, int *, void *);
static int nfsrpc_fsinfo(vnode_t, struct nfsfsinfo *, struct ucred *,
    NFSPROC_T *, struct nfsvattr *, int *, void *);
static int nfsrpc_pathconf(vnode_t, struct nfsv3_pathconf *,
    struct ucred *, NFSPROC_T *, struct nfsvattr *, int *, void *);
static int nfsrpc_renew(struct nfsclclient *, struct ucred *,
    NFSPROC_T *);
static int nfsrpc_rellockown(struct nfsmount *, struct nfscllockowner *,
    struct ucred *, NFSPROC_T *);
static int nfsrpc_getdirpath(struct nfsmount *, u_char *, struct ucred *,
    NFSPROC_T *);
static int nfsrpc_delegreturn(struct nfscldeleg *, struct ucred *,
    struct nfsmount *, NFSPROC_T *, int);

/* nfscl_state.c */
static int nfscl_open(vnode_t, u_int8_t *, int, u_int32_t, int,
    struct ucred *, NFSPROC_T *, struct nfsclowner **, struct nfsclopen **,
    int *, int *, int);
static int nfscl_getstateid(vnode_t, u_int8_t *, int, u_int32_t, struct ucred *,
    NFSPROC_T *, nfsv4stateid_t *, void **);
static void nfscl_ownerrelease(struct nfsclowner *, int, int, int);
static void nfscl_openrelease(struct nfsclopen *, int, int);
static int nfscl_getcl(vnode_t, struct ucred *, NFSPROC_T *,
    struct nfsclclient **);
static struct nfsclclient *nfscl_findcl(struct nfsmount *);
static void nfscl_clientrelease(struct nfsclclient *);
static void nfscl_freecl(struct nfsclclient *);
static void nfscl_freelock(struct nfscllock *, int);
static int nfscl_getbytelock(vnode_t, u_int64_t, u_int64_t, short,
    struct ucred *, NFSPROC_T *, struct nfsclclient *, int, u_int8_t *,
    u_int8_t *, struct nfscllockowner **, int *, int *);
static int nfscl_relbytelock(vnode_t, u_int64_t, u_int64_t,
    struct ucred *, NFSPROC_T *, int, struct nfsclclient *,
    struct nfscllockowner **, int *);
static int nfscl_checkwritelocked(vnode_t, struct flock *,
    struct ucred *, NFSPROC_T *);
static void nfscl_lockrelease(struct nfscllockowner *, int, int);
static void nfscl_ownername(vnode_t, NFSPROC_T *, u_char *);
static void nfscl_fillclid(u_int64_t, u_int8_t *, union nethostaddr *);
static void nfscl_filllockowner(NFSPROC_T *, u_int8_t *);
static void nfscl_freeopen(struct nfsclopen *, int);
static void nfscl_cleanup(NFSPROC_T *);
static void nfscl_umount(struct nfsmount *, NFSPROC_T *);
static void nfscl_renewthread(struct nfsclclient *, NFSPROC_T *);
static void nfscl_initiate_recovery(struct nfsclclient *);
static int nfscl_hasexpired(struct nfsclclient *, u_int32_t, NFSPROC_T *);
static void nfscl_dumpstate(struct nfsmount *, int, int, int, int);
static void nfscl_dupopen(vnode_t, int);
static int nfscl_getclose(vnode_t, struct nfsclclient **);
static int nfscl_doclose(vnode_t, struct nfsclclient **, NFSPROC_T *);
static int nfscl_deleg(mount_t, struct nfsclclient *, u_int8_t *, int,
    struct ucred *, NFSPROC_T *, struct nfscldeleg **);
static void nfscl_lockinit(struct nfsv4lock *);
static void nfscl_lockexcl(struct nfsv4lock *, void *);
static void nfscl_lockunlock(struct nfsv4lock *);
static void nfscl_lockderef(struct nfsv4lock *);
static void nfscl_docb(struct nfsrv_descript *, NFSPROC_T *);
static void nfscl_releasealllocks(struct nfsclclient *, vnode_t, NFSPROC_T *);
static int nfscl_lockt(vnode_t, struct nfsclclient *, u_int64_t,
    u_int64_t, struct flock *, NFSPROC_T *);
static int nfscl_mustflush(vnode_t);
static int nfscl_nodeleg(vnode_t, int);
static int nfscl_removedeleg(vnode_t, NFSPROC_T *, nfsv4stateid_t *);
static int nfscl_getref(struct nfsmount *);
static void nfscl_relref(struct nfsmount *);
static int nfscl_renamedeleg(vnode_t, nfsv4stateid_t *, int *, vnode_t,
    nfsv4stateid_t *, int *, NFSPROC_T *);
static void nfscl_reclaimnode(vnode_t);
static void nfscl_newnode(vnode_t);
static void nfscl_delegmodtime(vnode_t);
static void nfscl_deleggetmodtime(vnode_t, struct timespec *);
static int nfscl_tryclose(struct nfsclopen *, struct ucred *,
    struct nfsmount *, NFSPROC_T *);

/* nfscl_port.c */
static int nfscl_nget(mount_t, vnode_t, struct nfsfh *,
    struct componentname *, NFSPROC_T *, struct nfsnode **, void *);
static NFSPROC_T *nfscl_getparent(NFSPROC_T *);
static void nfscl_start_renewthread(struct nfsclclient *);
static void nfscl_loadsbinfo(struct nfsmount *, struct nfsstatfs *, void *);
static void nfscl_loadfsinfo (struct nfsmount *, struct nfsfsinfo *);
static int nfscl_getattr(vnode_t, struct nfs_vattr *, struct ucred *, NFSPROC_T *);
static int nfsgsscl_addport(u_short, uid_t, NFSPROC_T *);
static void nfsgsscl_deleteport(uid_t);
static void nfscl_delegreturn(struct nfscldeleg *, int, struct nfsmount *,
    struct ucred *, NFSPROC_T *);
static int nfssvc_cbaddsock(struct file *, NFSSOCKADDR_T, NFSPROC_T *);
static int nfsrvd_nfscbd(NFSPROC_T *);
static void nfsrvd_cbinit(int, NFSPROC_T *);
static int nfscl_checksattr(struct vattr *, struct nfsvattr *);
static int nfscl_ngetreopen(mount_t, u_int8_t *, int, NFSPROC_T *,
    struct nfsnode **);
static void nfscl_init(void);
static int nfscl_procdoesntexist(u_int8_t *);
static int nfscl_renewproc(proc_t, struct nfssvc_args *);
static int nfscl_asyncioproc(proc_t, struct nfssvc_args *);
static void newnfs_iod_continue(struct nfsmount *, struct nfsasynciothread *,
    proc_t);
static void nfscl_start_iothread(struct nfsmount *);
static void start_nfsio(void);
static int nfs_catnap(int, const char *);

/* ncl_bio.c */
static int ncl_flush(vnode_t, int, struct ucred *, NFSPROC_T *, int);
static int ncl_bioread(struct vnode *, struct uio *, int, struct ucred *, proc_t);
static int ncl_biowrite(struct vnode *, struct uio *, int, struct ucred *);
static int ncl_vinvalbuf(struct vnode *, int, struct ucred *, struct proc *,
		       int);
static int ncl_asyncio(struct nfsbuf *, struct ucred *, struct nfsmount *);
static int ncl_doio(struct nfsbuf *, struct ucred *, proc_t);
static int ncl_buf_write(struct nfsbuf *);
static void ncl_buf_write_delayed(struct nfsbuf *, proc_t);
static int ncl_buf_page_inval(vnode_t vp, off_t offset);
static void ncl_buf_check_write_verifier(struct nfsnode *, struct nfsbuf *);

/* ncl_node.c */
static void ncl_nhinit(void);
static int ncl_nget(mount_t, vnode_t, struct componentname *, u_char *,
    int, struct nfsvattr *, u_int64_t *, int, struct nfsnode **);

/* ncl_vfsops.c */
static int	ncl_fsinfo(struct nfsmount *, vnode_t, struct ucred *, proc_t p);
static int	newnfs_mountroot(void);

/* ncl_vnops.c */
static int	ncl_pathconfrpc(vnode_t, struct nfsv3_pathconf *, kauth_cred_t, proc_t);
static void	ncl_pathconf_cache(struct nfsmount *, struct nfsv3_pathconf *);
static int	ncl_getattr(vnode_t vp, struct nfs_vattr *nvap,
		    struct ucred *cred, proc_t p, kauth_acl_t *);
static int	ncl_readrpc(vnode_t, struct uio *, struct ucred *, proc_t);
static int	ncl_writerpc(vnode_t, struct uio *, struct ucred *, proc_t, int *, u_int64_t *);
static int	ncl_writerpcdirect(vnode_t, struct uio *, struct ucred *, proc_t);
static int	ncl_commit(vnode_t vp, u_quad_t offset, u_int32_t count,
			struct ucred *cred, proc_t procp);
static int	ncl_flushcommits(vnode_t, proc_t, int);
static int	ncl_readlinkrpc(vnode_t, struct uio *, struct ucred *, proc_t);
static int	ncl_readdirrpc(vnode_t, struct uio *, struct ucred *, proc_t);
static int	ncl_readdirplusrpc(vnode_t, struct uio *, struct ucred *, proc_t);

/* ncl_boot.c */

/* ncl_kq.c */
static int  ncl_kqfilter(void *);
static void ncl_kqinit(void);

/* ncl_subs.c */
static int ncl_init(struct vfsconf *);
static void ncl_mbuf_init(void);
static int ncl_getattrcache(vnode_t, struct nfs_vattr *);
static int ncl_attrcachetimeout(vnode_t);

/* ncl_lock. */
static int ncl_dolock(struct vnop_advlock_args *ap);
static void ncl_getset_niothreads(int);

static void start_nclio(void *);

static int	ncl_inactive(struct vnop_inactive_args *);
static int	ncl_reclaim(struct vnop_reclaim_args *);
static int	ncl_write(struct vnop_write_args *);

/* other stuff */
static int	ncl_removeit(struct sillyrename *, vnode_t);
static int	nfs_nget(mount_t,vnode_t,struct componentname *,u_char *,int,struct nfsvattr *,u_int64_t *,int,struct nfsnode **);
static nfsuint64 *ncl_getcookie(struct nfsnode *, off_t, int64_t, int);
static void ncl_invaldir(vnode_t);

/* nfsbuf functions */
static void ncl_nbinit(void);
static void ncl_buf_remfree(struct nfsbuf *);
static boolean_t ncl_buf_is_incore(vnode_t, daddr64_t);
static struct nfsbuf * ncl_buf_incore(vnode_t, daddr64_t);
static int ncl_buf_get(vnode_t, daddr64_t, int, proc_t, int, struct nfsbuf **);
static int ncl_buf_upl_setup(struct nfsbuf *bp);
static void ncl_buf_upl_check(struct nfsbuf *bp);
static void ncl_buf_release(struct nfsbuf *, int);
static int ncl_buf_iowait(struct nfsbuf *);
static void ncl_buf_iodone(struct nfsbuf *);
static void ncl_buf_freeup(int);
static void ncl_buf_refget(struct nfsbuf *bp);
static void ncl_buf_refrele(struct nfsbuf *bp);
static void ncl_buf_drop(struct nfsbuf *);
static errno_t ncl_buf_acquire(struct nfsbuf *, int, int, int);
static int ncl_buf_iterprepare(struct nfsnode *, struct nfsbuflists *, int);
static void ncl_buf_itercomplete(struct nfsnode *, struct nfsbuflists *, int);

/*
 * Now, define all the global storage as static.
 */
static int nfstest_dirbypassbcache = 1;
static struct nfsbuffreehead nclbufdelwri;
static int nclbufcnt, nclbufmin, nclbufmax, nclbufmetacnt, nclbufmetamax;
static int nclbuffreecnt, nclbuffreemetacnt, nclbufdelwricnt, nclneedbuffer;
static int ncl_nbdwrite;
static time_t nclbuffreeuptimestamp;
lck_mtx_t *ncl_buf_mutex;
static u_long newnfsnodehash;
static LIST_HEAD(nfsnodehashhead, nfsnode) *newnfsnodehashtbl;
static lck_grp_t * ncl_node_hash_lck_grp;
static lck_grp_attr_t * ncl_node_hash_lck_grp_attr;
static lck_attr_t * ncl_node_hash_lck_attr;
lck_mtx_t *ncl_node_hash_mutex;
static int ncl_mount_type, ncl_resv_mounts = 0;
lck_mtx_t *ncl_iod_mutex;
static struct nfsstats	newnfsstats;
static proc_t ncl_iodwant[NFS_MAXASYNCDAEMON];
static struct nfsmount *ncl_iodmount[NFS_MAXASYNCDAEMON];
static int ncl_ioddelwri = 0;
static LIST_HEAD(nfsgsshash, nfsgss_handle) nfsgsshashtbl[NFSGSSCACHE_HASHSIZE];
static TAILQ_HEAD(nfsgsslru, nfsgss_handle) nfsgsslruhead;
static struct nfsgssclsockhead nfsgssclsockhead;
static int nfsgss_highwater = NFSGSS_HIGHWATER;
static int nfsgss_numgssd = 0;
static u_int32_t nfsgsscl_cnt = 0;
static u_int32_t nfsgsscl_highwater = NFSGSSCLHIGHWATER;
static u_int32_t nfsgsscl_seqmisscnt = 0;
static char *nfsv4_callbackaddr = "";

/*
 * This list defines the GSS mechanisms supported.
 * (Don't ask me how you get these strings from the RFC stuff like
 *  iso(1), org(3)... but someone did it, so I don't need to know.)
 */
static struct nfsgss_mechlist nfsgss_mechlist[] = {
	{ 9, "\052\206\110\206\367\022\001\002\002", 11 },
	{ 0, "", 0 },
};

lck_mtx_t *nfs_gss_mutex;
lck_mtx_t *nfs_gssclhandle_mutex;
lck_spin_t *nfs_nameid_slock;
lck_mtx_t *newnfsd_mtx;
lck_spin_t *nfs_state_slock;
lck_mtx_t *nfs_sockl_mutex;
lck_mtx_t *nfs_req_slock;
static lck_rw_t newnfs_export_rwlock;
static struct newnfs_sockhead newnfs_deadsockhead;
static struct newnfs_sockhead newnfscb_deadsockhead;
static int nfs_numnfscbd = 0;
static int nfsrv_testmalloclimit = 0;
static int nfstest_dropreplies = 0;
static int nfstest_v4udp = 0;
static int newnfsrtton = 0;
static struct nfsrtt newnfsrtt;

/*
 * This static array of structures indicates, for V4:
 * retfh - which of 3 types of calling args are used
 *	0 - doesn't change cfh or use a sfh
 *	1 - replaces cfh with a new one (unless it returns an error status)
 *	2 - uses cfh and sfh
 * needscfh - if the op wants a cfh and premtime
 *	0 - doesn't use a cfh
 *	1 - uses a cfh, but doesn't want pre-op attributes
 *	2 - uses a cfh and wants pre-op attributes
 * savereply - indicates a non-idempotent Op
 *	0 - not non-idempotent
 *	1 - non-idempotent
 * Ops that are ordered via seqid# are handled separately from these
 * non-idempotent Ops.
 */
static struct nfsv4_opflag nfsv4_opflag[NFSV4OP_NOPS] = {
	{ 0, 0, 0, 0 },		/* undef */
	{ 0, 0, 0, 0 },		/* undef */
	{ 0, 0, 0, 0 },		/* undef */
	{ 0, 1, 0, 0 },		/* Access */
	{ 0, 1, 0, 0 },		/* Close */
	{ 0, 2, 0, 1 },		/* Commit */
	{ 1, 2, 1, 1 },		/* Create */
	{ 0, 0, 0, 0 },		/* Delegpurge */
	{ 0, 1, 0, 0 },		/* Delegreturn */
	{ 0, 1, 0, 0 },		/* Getattr */
	{ 0, 1, 0, 0 },		/* GetFH */
	{ 2, 1, 1, 1 },		/* Link */
	{ 0, 1, 0, 0 },		/* Lock */
	{ 0, 1, 0, 0 },		/* LockT */
	{ 0, 1, 0, 0 },		/* LockU */
	{ 1, 1, 0, 0 },		/* Lookup */
	{ 1, 1, 0, 0 },		/* Lookupp */
	{ 0, 1, 0, 0 },		/* NVerify */
	{ 1, 1, 0, 1 },		/* Open */
	{ 1, 1, 0, 0 },		/* OpenAttr */
	{ 0, 1, 0, 0 },		/* OpenConfirm */
	{ 0, 1, 0, 0 },		/* OpenDowngrade */
	{ 1, 0, 0, 0 },		/* PutFH */
	{ 1, 0, 0, 0 },		/* PutPubFH */
	{ 1, 0, 0, 0 },		/* PutRootFH */
	{ 0, 1, 0, 0 },		/* Read */
	{ 0, 1, 0, 0 },		/* Readdir */
	{ 0, 1, 0, 0 },		/* ReadLink */
	{ 0, 2, 1, 1 },		/* Remove */
	{ 2, 1, 1, 1 },		/* Rename */
	{ 0, 0, 0, 0 },		/* Renew */
	{ 0, 0, 0, 0 },		/* RestoreFH */
	{ 0, 1, 0, 0 },		/* SaveFH */
	{ 0, 1, 0, 0 },		/* SecInfo */
	{ 0, 2, 1, 1 },		/* Setattr */
	{ 0, 0, 0, 0 },		/* SetClientID */
	{ 0, 0, 0, 0 },		/* SetClientIDConfirm */
	{ 0, 1, 0, 0 },		/* Verify */
	{ 0, 2, 1, 1 },		/* Write */
	{ 0, 0, 0, 0 },		/* ReleaseLockOwner */
};

static u_int32_t newnfs_xdrneg1;
static u_int32_t newrpc_call, newrpc_vers, newrpc_reply, newrpc_msgdenied,
    newrpc_autherr, newrpc_mismatch, newrpc_msgaccepted;
static u_int32_t newnfs_prog, newnfs_true, newnfs_false;
static u_int32_t newnfs_xidwrap = 0;

static nfstype nfsv34_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK,
		      NFFIFO, NFNON };
static enum vtype newnv2tov_type[8] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON, VNON };
static enum vtype nv34tov_type[8]={ VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO };
static struct timeval nfsboottime;	/* Copy boottime once, so it never changes */
static int nfscl_ticks;
static int nfsrv_useacl = 1;
static struct nfssockreq nfsrv_nfsuserdsock;
static int nfsrv_nfsuserd = 0, nfsrv_usercnt = 0;
static struct nfsreqhead nfsd_reqq;
static int nfsrv_debug = 0;
static uid_t nfsrv_defaultuid;
static gid_t nfsrv_defaultgid;
static int nfsrv_dnsnamelen;
static u_char *nfsrv_dnsname = NULL;
static int nfsrv_lease = NFSRV_LEASE;
static time_t nfsrvboottime;
static int nfsrv_usermax = 999999999;
static struct nfsuserhashhead nfsuserhash[NFSUSERHASHSIZE];
static struct nfsuserhashhead nfsusernamehash[NFSUSERHASHSIZE];
static struct nfsuserhashhead nfsgrouphash[NFSGROUPHASHSIZE];
static struct nfsuserhashhead nfsgroupnamehash[NFSGROUPHASHSIZE];
static struct nfsuserlruhead nfsuserlruhead;

/*
 * and the reverse mapping from generic to Version 2 procedure numbers
 */
static int newnfsv2_procid[NFS_V3NPROCS] = {
	NFSV2PROC_NULL,
	NFSV2PROC_GETATTR,
	NFSV2PROC_SETATTR,
	NFSV2PROC_LOOKUP,
	NFSV2PROC_NOOP,
	NFSV2PROC_READLINK,
	NFSV2PROC_READ,
	NFSV2PROC_WRITE,
	NFSV2PROC_CREATE,
	NFSV2PROC_MKDIR,
	NFSV2PROC_SYMLINK,
	NFSV2PROC_CREATE,
	NFSV2PROC_REMOVE,
	NFSV2PROC_RMDIR,
	NFSV2PROC_RENAME,
	NFSV2PROC_LINK,
	NFSV2PROC_READDIR,
	NFSV2PROC_NOOP,
	NFSV2PROC_STATFS,
	NFSV2PROC_NOOP,
	NFSV2PROC_NOOP,
	NFSV2PROC_NOOP,
};

static struct nfssvc_sock *newnfs_cbudpsock;
lck_mtx_t *nfs_clstate_mutex;
lck_mtx_t *nfs_gsscl_mutex;
lck_spin_t *ncl_nfsnode_slock;

struct nfsrenewthread {
	LIST_ENTRY(nfsrenewthread)	next;
	struct nfsclclient		*clp;
};
static LIST_HEAD(nfsrenewthreadhead, nfsrenewthread) nfsrenewthreadhead;
static int nfstest_outofseq = 0;
static int nfscl_assumeposixlocks = 1;
static int nfscl_enablecallb = 0;
static short nfsv4_cbport = NFSV4_CBPORT;
static int nfstest_openallsetattr = 0;
static int nfscl_inited = 0;
static struct newnfsd_head newnfscbd_head;
static int newnfscbd_head_flag;
static struct newnfs_sockhead newnfscb_sockhead;
static int newnfscb_sockhead_flag;
static struct nfsclhead nfsclhead;	/* Head of clientid list */
static int nfscl_deleghighwater = NFSCLDELEGHIGHWATER;
static lck_grp_t *nfs_bind_resv_lck_grp;
static lck_grp_attr_t *nfs_bind_resv_lck_grp_attr;
static lck_attr_t *nfs_bind_resv_lck_attr;
lck_mtx_t *ncl_bind_resv_mutex;
struct nfsasynciothread {
	TAILQ_ENTRY(nfsasynciothread)	nfsio_list;
	struct nfsmount			*nfsio_nmp;
	int				nfsio_ready;
};
static TAILQ_HEAD(nfsasynciothreadhead, nfsasynciothread) nfsasynciothreadhead;

/* XXX */
boolean_t	current_thread_aborted(void);
kern_return_t	thread_terminate(thread_t);

/*
 * and finally, all the C source files.
 */
#include "nfsv4_errstr.h"
#include "newnfs_port.c"
#include "ncl_bio.c"
#include "ncl_vnops.c"
#include "ncl_node.c"
#include "ncl_subs.c"
#include "ncl_vfsops.c"
#include "newnfs_acl.c"
#include "newnfs_gss.c"
#include "newnfs_socket.c"
#include "newnfs_subs.c"
#include "newnfs_syscalls.c"
#include "nfscl_port.c"
#include "nfscl_rpcops.c"
#include "nfscl_state.c"
#include "nfscl_subs.c"
#include "cbc_cksm.c"
#include "cbc_enc.c"

/*
 * Had to snitch this function out of the kernel sources, since it is
 * __private_extern__.
 */
/*
 * uio_createwithbuffer - create an uio_t.
 * 	Create a uio_t using the given buffer.  The uio_t
 *	is not fully initialized until all iovecs are added using uio_addiov calls.
 *	a_iovcount is the maximum number of iovecs you may add.
 *	This call may fail if the given buffer is not large enough.
 */
uio_t 
	uio_createwithbuffer( int a_iovcount,		/* number of iovecs */
				  			off_t a_offset,		/* current offset */
				  			int a_spacetype,	/* type of address space */
				 			int a_iodirection,	/* read or write flag */
				 			void *a_buf_p,		/* pointer to a uio_t buffer */
				 			int a_buffer_size )	/* size of uio_t buffer */
{
	uio_t				my_uio = (uio_t) a_buf_p;
	int					my_size;
	
	my_size = sizeof(struct uio) + (sizeof(struct user_iovec) * a_iovcount);
	if (a_buffer_size < my_size) {
#if DEBUG
		panic("%s :%d - a_buffer_size is too small\n", __FILE__, __LINE__); 
#endif /* DEBUG */
		return( NULL );
	}
	my_size = a_buffer_size;
	
#if DEBUG
	if (my_uio == 0) {
		panic("%s :%d - could not allocate uio_t\n", __FILE__, __LINE__); 
	}
	if (!IS_VALID_UIO_SEGFLG(a_spacetype)) {
		panic("%s :%d - invalid address space type\n", __FILE__, __LINE__); 
	}
	if (!(a_iodirection == UIO_READ || a_iodirection == UIO_WRITE)) {
		panic("%s :%d - invalid IO direction flag\n", __FILE__, __LINE__); 
	}
	if (a_iovcount > UIO_MAXIOV) {
		panic("%s :%d - invalid a_iovcount\n", __FILE__, __LINE__); 
	}
#endif /* DEBUG */

	bzero(my_uio, my_size);
	my_uio->uio_size = my_size;

	/* we use uio_segflg to indicate if the uio_t is the new format or */
	/* old (pre LP64 support) legacy format */
	switch (a_spacetype) {
	case UIO_USERSPACE:
		my_uio->uio_segflg = UIO_USERSPACE32;
	case UIO_SYSSPACE:
		my_uio->uio_segflg = UIO_SYSSPACE32;
	case UIO_PHYS_USERSPACE:
		my_uio->uio_segflg = UIO_PHYS_USERSPACE32;
	case UIO_PHYS_SYSSPACE:
		my_uio->uio_segflg = UIO_PHYS_SYSSPACE32;
	default:
		my_uio->uio_segflg = a_spacetype;
		break;
	}

	if (a_iovcount > 0) {
		my_uio->uio_iovs.uiovp = (struct user_iovec *)
			(((uint8_t *)my_uio) + sizeof(struct uio));
	}
	else {
		my_uio->uio_iovs.uiovp = NULL;
	}

	my_uio->uio_max_iovs = a_iovcount;
	my_uio->uio_offset = a_offset;
	my_uio->uio_rw = a_iodirection;
	my_uio->uio_flags = UIO_FLAGS_INITED;

	return( my_uio );
}

/*
 * stuff to wire into the Leopard gssd. Some cribbed straight out of xnu-1228.
 */
/*
 * Here we handle a token received from the gssd via an upcall.
 * The received token resides in an allocate VM buffer.
 * We copy the token out of this buffer to a chunk of malloc'ed
 * memory of the right size, then de-allocate the VM buffer.
 */
static int
nfs_gss_mach_vmcopyout(vm_map_copy_t in, uint32_t len, u_char *out)
{
	vm_map_offset_t map_data;
	vm_offset_t data;
	int error;

	error = vm_map_copyout(ipc_kernel_map, &map_data, in);
	if (error)
		return (error);

	data = CAST_DOWN(vm_offset_t, map_data);
	bcopy((void *) data, out, len);
	vm_deallocate(ipc_kernel_map, data, len);

	return (0);
}

extern void ipc_port_release_send(ipc_port_t);

static void
task_release_special_port(mach_port_t mp)
{

	ipc_port_release_send(mp);
}

/*
 * The token that is sent and received in the gssd upcall
 * has unbounded variable length.  Mach RPC does not pass
 * the token in-line.  Instead it uses page mapping to handle
 * these parameters.  This function allocates a VM buffer
 * to hold the token for an upcall and copies the token
 * (received from the client) into it.  The VM buffer is
 * marked with a src_destroy flag so that the upcall will
 * automatically de-allocate the buffer when the upcall is
 * complete.
 */
static void
nfs_gss_mach_alloc_buffer(u_char *buf, uint32_t buflen, vm_map_copy_t *addr)
{
	kern_return_t kr;
	vm_offset_t kmem_buf;
	vm_size_t tbuflen;

	*addr = NULL;
	if (buf == NULL || buflen == 0)
		return;

	tbuflen = round_page(buflen);
	kr = vm_allocate(ipc_kernel_map, &kmem_buf, tbuflen, VM_FLAGS_ANYWHERE);
	if (kr != 0) {
		printf("nfs_gss_mach_alloc_buffer: vm_allocate failed\n");
		return;
	}

	kr = vm_map_wire(ipc_kernel_map, vm_map_trunc_page(kmem_buf),
		vm_map_round_page(kmem_buf + tbuflen),
		VM_PROT_READ|VM_PROT_WRITE, FALSE);

	bcopy(buf, (void *) kmem_buf, buflen);

	kr = vm_map_unwire(ipc_kernel_map, vm_map_trunc_page(kmem_buf),
		vm_map_round_page(kmem_buf + tbuflen), FALSE);
	if (kr != 0) {
		printf("nfs_gss_mach_alloc_buffer: vm_map_unwire failed\n");
		return;
	}

	kr = vm_map_copyin(ipc_kernel_map, (vm_map_address_t) kmem_buf,
		(vm_map_size_t) buflen, TRUE, addr);
	if (kr != 0) {
		printf("nfs_gss_mach_alloc_buffer: vm_map_copyin failed\n");
		return;
	}

	if (buflen != tbuflen)
		kmem_free(ipc_kernel_map, kmem_buf + buflen, tbuflen - buflen);
}

/*
 * This function is modified to work with the Leopard gssd for the client
 * case. It's ugly, but no uglier than the original, I suppose.
 */
static int
nfsgss_getclhandle(struct nfsreq *rep, u_char *name, int namelen, uid_t uid,
    u_int32_t mechflag, int clientside)
{
	u_int32_t *tl;
	struct nfsrv_descript *nd;
	u_char *cp;
	struct nfsgss_handleref *handp = rep->r_handp;
	struct nfsgss_handlebuf *handb = rep->r_handb;
	struct nfsrv_descript nfsd;
	struct ucred cr;
	int error, err, i;
	u_int32_t major = 0, srvmajor = GSS_S_CONTINUE_NEEDED, winsize = 0;
	u_int32_t tokenlen = 0, skeylen = 0, gssdctx = 0, gssdcredhandle = 0;
	u_int32_t minor = 0;
	u_int64_t gssdverf = 0;
	u_char retsum[8], calcsum[8];
	kern_return_t kr;
	byte_buffer okey = NULL;
	vm_map_copy_t itoken = NULL;
	byte_buffer otoken = NULL;
	mach_port_t mport;
	struct nfsgssclsock *ngcp = NULL;
	char uprinc[1], *iamclient;

	if (!clientside || namelen != 0 || rep->r_nmp == NULL)
		return (EAUTH);
	if ((handp->nfsh_flag & NFSG_LOCKED) == 0)
		panic("nfs getclhandle");

	/*
	 * If nm_uid != -1, it has been set to the real uid of the
	 * user that is using this mount and that user should have
	 * a valid TGT in the credentials cache. Allow system
	 * processes to use this credential, whereas have other
	 * users (typically snoopy daemons in Leopard) to fail now,
	 * so that they don't confuse the Leopard gssd by attempting
	 * an upcall. The upcall will fail, since they won't have
	 * access to the Leopard credentials cache, that lives in
	 * Mach VM somehow.
	 */
	if (rep->r_nmp->nm_uid != (uid_t)-1) {
		if (uid == 0)
			uid = rep->r_nmp->nm_uid;
		else if (uid != rep->r_nmp->nm_uid)
			return (EAUTH);
	}

	/*
	 * Fake the creation of one gssclsock structure, so the correct
	 * uid is always used.
	 */
	MALLOC(ngcp, struct nfsgssclsock *, sizeof (struct nfsgssclsock),
	    M_NFSGSSCL, M_WAITOK);
	NFSLOCKGSSCL;
	if (LIST_EMPTY(&nfsgssclsockhead)) {
		ngcp->nfsgsscl_uid = uid;
		LIST_INSERT_HEAD(&nfsgssclsockhead, ngcp, nfsgsscl_list);
		ngcp = NULL;
	}
	NFSUNLOCKGSSCL;
	if (ngcp != NULL)
		FREE(ngcp, M_NFSGSSCL);

	/*
	 * Get a mach port for the initialization.
	 * Just cribbed from xnu-1228/bsd/nfs/nfs_gss.c.
	 */
	kr = task_get_gssd_port(get_threadtask(current_thread()), &mport);
	if (kr != KERN_SUCCESS) {
		printf("nfsgss_getclhandle: can't get gssd port, status %d\n", kr);
		return (EAUTH);
	}
	if (!IPC_PORT_VALID(mport)) {
		printf("nfsgss_getclhandle: gssd port not valid\n");
		return (EAUTH);
	}

	nd = &nfsd;
	nd->nd_mrep = NULL;
	cr.cr_uid = uid;
	cr.cr_gid = 0;
	cr.cr_groups[0] = 0;
	cr.cr_ngroups = 1;
	uprinc[0] = '\0';
	iamclient = "newnfs";

	/*
	 * Loop around doing Init and Continue Inits, until done.
	 */
	do {
	    /*
	     * Do the initial or continue init upcall to the gssd.
	     */
	    kr = mach_gss_init_sec_context(mport, KRB5_MECH,
		(byte_buffer)itoken, (mach_msg_type_number_t)tokenlen,
		uid, uprinc, NFSMNT_SVCNAME(rep->r_nmp),
		GSSD_MUTUAL_FLAG | GSSD_NO_UI, &gssdverf,
		&gssdctx, &gssdcredhandle, &okey,
		(mach_msg_type_number_t *)&skeylen, &otoken,
		(mach_msg_type_number_t *)&tokenlen, &major, &minor);
	    if (kr != KERN_SUCCESS)
		major = GSS_S_FAILURE;
	    else if (major != GSS_S_COMPLETE && major != GSS_S_CONTINUE_NEEDED)
		(void) mach_gss_log_error(mport,
		    vfs_statfs(rep->r_nmp->nm_mountp)->f_mntfromname,
		    uid, iamclient, major, minor);
	    if (major == GSS_S_COMPLETE) {
		/*
		 * For Complete, the gssd replies with the session key.
		 */
	    	/*
	    	 * XXX For now, must be RPCGSS_KEYSIZ in size (a DES key).
	    	 */
	    	if (skeylen != RPCGSS_KEYSIZ)
	    		panic("nfsgss keysize");
		err = nfs_gss_mach_vmcopyout((vm_map_copy_t)okey,
		    skeylen, handp->nfsh_key);
		if (err)
			major = GSS_S_FAILURE;
	    	KEYSCHED(handp->nfsh_key, handp->nfsh_sched);
	    }
	    if ((major == GSS_S_COMPLETE || major == GSS_S_CONTINUE_NEEDED) &&
		srvmajor == GSS_S_CONTINUE_NEEDED) {
		/*
		 * Now, do the Null RPC with the gss token as data.
		 */
		nd->nd_flag = (mechflag | ND_GSSINITREPLY);
		if ((rep->r_flags & R_USEGSSNAME))
			nd->nd_flag |= ND_USEGSSNAME;
		if (tokenlen > 0 && tokenlen < 1000) {
			MALLOC(cp, u_char *, tokenlen, M_TEMP, M_WAITOK);
			err = nfs_gss_mach_vmcopyout((vm_map_copy_t)otoken,
			    tokenlen, cp);
			if (!err) {
				nfsrvd_rephead(nd);
				(void) nfsm_strtom(nd, cp, tokenlen);
			} else {
				major = GSS_S_FAILURE;
			}
			FREE(cp, M_TEMP);
		} else {
			major = GSS_S_FAILURE;
		}
		nd->nd_procnum = NFSPROC_NULL;
		if (major != GSS_S_FAILURE) {
			error = newnfs_request(nd, rep->r_nmp, rep->r_clp, rep->r_nrp,
			    NULL, NULL, &cr, rep->r_prog, rep->r_vers, retsum, 0, NULL);
			if (error) {
				srvmajor = GSS_S_FAILURE;
				nd->nd_mrep = NULL;
			} else {
				/*
				 * Parse the reply, getting the handle and, if
				 * Complete, the window size.
				 */
				NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
				i = fxdr_unsigned(int, *tl);
				if (i > 0) {
				    /*
				     * Handles that are > NFSGSS_HANDLESIZ must
				     * be malloc'd.
				     * First, free any large one that is already
				     * malloc'd here.
				     * If the handle len is already correct, we're
				     * happy campers.
				     */
				    if (handb->handlelen != i) {
					if (handb->handlelen > NFSGSS_HANDLESIZ)
					    FREE(handb->handle, M_NFSGSSHANDLE);
					if (i > NFSGSS_HANDLESIZ)
					    handb->handle = (u_char *)malloc(i,
						M_NFSGSSHANDLE, M_WAITOK);
					else
					    handb->handle = handb->smhandle;
					handb->handlelen = i;
				    }
				    NFSM_DISSECT(cp, u_char *, NFSM_RNDUP(i));
				    NFSBCOPY(cp, handb->handle, i);
				}
				NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
				srvmajor = fxdr_unsigned(u_int32_t, *tl++);
				if (srvmajor == GSS_S_COMPLETE)
					winsize = fxdr_unsigned(u_int32_t, *++tl);
			}
		}
		if (srvmajor == GSS_S_COMPLETE) {
			/*
			 * If Complete, set up the window.
			 */
			handp->nfsh_winsize =
				(winsize > RPCAUTHGSS_SEQWINDOW) ?
				RPCAUTHGSS_SEQWINDOW : winsize;
			handp->nfsh_curwin = handp->nfsh_seqnum =
				arc4random() % 10000;
			handp->nfsh_window = 0;
		}
	    }

	    if (major == GSS_S_CONTINUE_NEEDED) {
		if (srvmajor == GSS_S_COMPLETE ||
		    srvmajor == GSS_S_CONTINUE_NEEDED) {
			newnfs_trimleading(nd);
			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
			tokenlen = fxdr_unsigned(u_int32_t, *tl);
			if (tokenlen > 0 && tokenlen < 1000) {
				MALLOC(cp, u_char *, tokenlen + 1, M_TEMP,
				    M_WAITOK);
				error = nfsrv_mtostr(nd, cp, tokenlen);
				if (error) {
					FREE(cp, M_TEMP);
					goto nfsmout;
				}
				nfs_gss_mach_alloc_buffer(cp, tokenlen,
				    &itoken);
				FREE(cp, M_TEMP);
			} else {
				srvmajor = GSS_S_FAILURE;
			}
		}
		handp->nfsh_flag &= ~NFSG_INIT;
		handp->nfsh_flag |= NFSG_CONTINIT;
	    } else if (major == GSS_S_COMPLETE) {
		if (srvmajor == GSS_S_COMPLETE) {
		    /*
		     * For Complete, check the verifier.
		     */
		    nfsgss_calcverf(ND_KERBV, handp->nfsh_sched, winsize,
			calcsum);
		    if (NFSBCMP(calcsum, retsum, 8)) {
			srvmajor = GSS_S_FAILURE;
		    } else {
			handp->nfsh_flag &= ~(NFSG_INIT | NFSG_CONTINIT |
			    NFSG_ZAPHANDLE);
			handp->nfsh_flag |= NFSG_COMPLETE;
		    }
		}
	    }
	    if (nd->nd_mrep) {
		mbuf_freem(nd->nd_mrep);
		nd->nd_mrep = NULL;
	    }
	} while (major == GSS_S_CONTINUE_NEEDED);
	if (major == GSS_S_COMPLETE && srvmajor == GSS_S_COMPLETE) {
		task_release_special_port(mport);
		return (0);
	}
	error = EAUTH;
nfsmout:
	task_release_special_port(mport);
	if (handb->handlelen > NFSGSS_HANDLESIZ)
		FREE(handb->handle, M_NFSGSSHANDLE);
	handb->handlelen = 0;
	handb->handle = NULL;
	handp->nfsh_flag &= ~(NFSG_INIT | NFSG_CONTINIT);
	if (nd->nd_mrep)
		mbuf_freem(nd->nd_mrep);
	return (error);
}

/*
 * Finally, the kext startup and shutdown. Since I don't know how to add
 * a system call, I hijack the nfssvc syscall. Yuck!
 */
static vfstable_t newnfs_table;
struct vnodeopv_desc *newnfs_opv[3] = {
	&newnfs_vnodeop_opv_desc,
	&spec_newnfsnodeop_opv_desc,
	&fifo_newnfsnodeop_opv_desc
};
struct vfs_fsentry newnfs_fs = {
	&newnfs_vfsops,
	3,
	&newnfs_opv[0],
	2,
	"newnfs",
	VFS_TBLNOTYPENUM,
	NULL,
	NULL,
};
static struct sysent *mysysentptr = NULL;
extern int kdebug_chudhook;

kern_return_t
newnfs_start(kmod_info_t * ki, void * d)
{
	errno_t ret;
	int *tptr;

	/*
	 * Holy Bogosity Batman!. To hijack the nfssvc syscall,
	 * I need to fiddle with the sysent table, but it's defined
	 * __private_extern__, so I use nsysent to get a pointer to it.
	 * The trick is that nsysent is right next to the table, either
	 * just before or just after it. (Not right after it, though:-)
	 * First, try just before and then just after.
	 */
	if (mysysentptr == NULL) {
		mysysentptr = (struct sysent *)&nsysent;
		mysysentptr -= nsysent;
		if (mysysentptr[SYS_nfssvc].sy_call != (sy_call_t *)nfssvc) {
			tptr = &nsysent;
			do {
				tptr++;
				mysysentptr = (struct sysent *)tptr;
			} while (mysysentptr[SYS_nfssvc].sy_call !=
			    (sy_call_t *)nfssvc && tptr < &kdebug_chudhook);
		}
		if (mysysentptr[SYS_nfssvc].sy_call != (sy_call_t *)nfssvc) {
			mysysentptr = NULL;
			printf("couldn't hijack nfssvc\n");
			return (EINVAL);
		}
	}
	ret = vfs_fsadd(&newnfs_fs, &newnfs_table);
	if (ret)
		return (ret);
	mysysentptr[SYS_nfssvc].sy_call = (sy_call_t *)newnfssvc;

	printf("newnfs loaded\n");
	return (KERN_SUCCESS);
}

kern_return_t
newnfs_stop(kmod_info_t * ki, void * d)
{
	errno_t ret;

	/*
	 * If any daemon threads are still in the kernel, return EBUSY.
	 */
	if (nfs_numnfscbd != 0 || nfsgss_numgssd != 0 || nfsrv_nfsuserd != 0)
		return (EBUSY);

	/* Canncel the timer. */
	untimeout(newnfs_timer_funnel, (void *)0);

	/*
	 * and remove the file system type. If any mounts exist, this
	 * will return EBUSY.
	 */
	ret = vfs_fsremove(newnfs_table);
	if (ret)
		return (ret);

	/* and finally, un-hijack the nfssvc syscall. */
	if (mysysentptr != NULL)
		mysysentptr[SYS_nfssvc].sy_call = (sy_call_t *)nfssvc;
	printf("newnfs unloaded\n");
	return (KERN_SUCCESS);
}
